// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#include "mymuduo/base/ProcessInfo.h"
#include "mymuduo/base/CurrentThread.h"
#include "mymuduo/base/FileUtil.h"

#include <algorithm>

#include <assert.h>
#include <dirent.h>
#include <pwd.h>
#include <stdio.h> // snprintf
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/times.h>

namespace mymuduo
{
  namespace detail
  {
    // 保存某个进程打开的文件数
    __thread int t_numOpenedFiles = 0;

    // struct dirent
    //{
    //   long d_ino;               /* inode number 索引节点号 */
    //   off_t d_off;              /* offset to this dirent 在目录文件中的偏移 */
    //   unsigned short d_reclen;  /* length of this d_name 文件名长 */
    //   unsigned char d_type;     /* the type of d_name 文件类型 */
    //   char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名，最长255字符 */
    //}

    // scandir 的过滤器
    int fdDirFilter(const struct dirent *d)
    {
      if (::isdigit(d->d_name[0]))
      {
        ++t_numOpenedFiles;
      }
      return 0;
    }

    // 保存某个进程所有线程id
    __thread std::vector<pid_t> *t_pids = NULL;
    // 获取进程每一个线程的过滤条件，可以在过滤时遍历所有线程id，从而进行记录
    int taskDirFilter(const struct dirent *d)
    {
      if (::isdigit(d->d_name[0]))
      {
        t_pids->push_back(atoi(d->d_name));
      }
      return 0;
    }

    // 扫描目录
    int scanDir(const char *dirpath, int (*filter)(const struct dirent *))
    {
      struct dirent **namelist = NULL;
      int result = ::scandir(dirpath, &namelist, filter, alphasort);
      assert(namelist == NULL);
      return result;
    }

    Timestamp g_startTime = Timestamp::now();
    // assume those won't change during the life time of a process.
    int g_clockTicks = static_cast<int>(::sysconf(_SC_CLK_TCK)); // 时钟频率
    int g_pageSize = static_cast<int>(::sysconf(_SC_PAGE_SIZE)); // 内存页大小

  } // namespace detail
} // namespace mymuduo

using namespace mymuduo;
using namespace mymuduo::detail;

pid_t ProcessInfo::pid()
{
  return ::getpid();
}

string ProcessInfo::pidString()
{
  char buf[32];
  snprintf(buf, sizeof buf, "%d", pid());
  return buf;
}

uid_t ProcessInfo::uid()
{
  return ::getuid();
}

/**  获取用户和组的信息
 * 使用函数getpwnam()和getpwuid()的作用是从密码文件中获取记录。
 *    struct passwd *getpwnam(const char *name)
 *    struct passwd *getpwuid(uid_t uid)
 * getpwnam()会返回一个指针，指向如下类型结构
 */
// struct passwd
// {
//     char * pw_name; /* Username, POSIX.1 */
//     char * pw_passwd; /* Password */
//     __uid_t pw_uid; /* User ID, POSIX.1 */
//     __gid_t pw_gid; /* Group ID, POSIX.1 */
//     char * pw_gecos; /* Real Name or Comment field */
//     char * pw_dir; /* Home directory, POSIX.1 */
//     char * pw_shell; /* Shell Program, POSIX.1 */
// };

string ProcessInfo::username()
{
  struct passwd pwd;
  struct passwd *result = NULL;
  char buf[8192];
  const char *name = "unknownuser";

  getpwuid_r(uid(), &pwd, buf, sizeof buf, &result);
  if (result)
  {
    name = pwd.pw_name;
  }
  return name;
}

uid_t ProcessInfo::euid()
{
  return ::geteuid();
}

Timestamp ProcessInfo::startTime()
{
  return g_startTime;
}

int ProcessInfo::clockTicksPerSecond()
{
  return g_clockTicks;
}

int ProcessInfo::pageSize()
{
  return g_pageSize;
}

bool ProcessInfo::isDebugBuild()
{
#ifdef NDEBUG
  return false;
#else
  return true;
#endif
}

string ProcessInfo::hostname()
{
  // HOST_NAME_MAX 64
  // _POSIX_HOST_NAME_MAX 255
  char buf[256];
  if (::gethostname(buf, sizeof buf) == 0)
  {
    buf[sizeof(buf) - 1] = '\0';
    return buf;
  }
  else
  {
    return "unknownhost";
  }
}

string ProcessInfo::procname()
{
  return procname(procStat()).as_string();
}

StringPiece ProcessInfo::procname(const string &stat)
{
  StringPiece name;
  size_t lp = stat.find('(');
  size_t rp = stat.rfind(')');
  if (lp != string::npos && rp != string::npos && lp < rp)
  {
    name.set(stat.data() + lp + 1, static_cast<int>(rp - lp - 1));
  }
  return name;
}

string ProcessInfo::procStatus()
{
  string result;
  FileUtil::readFile("/proc/self/status", 65536, &result);
  return result;
}

string ProcessInfo::procStat()
{
  string result;
  FileUtil::readFile("/proc/self/stat", 65536, &result);
  return result;
}

string ProcessInfo::threadStat()
{
  char buf[64];
  snprintf(buf, sizeof buf, "/proc/self/task/%d/stat", CurrentThread::tid());
  string result;
  FileUtil::readFile(buf, 65536, &result);
  return result;
}

string ProcessInfo::exePath()
{
  string result;
  char buf[1024];
  ssize_t n = ::readlink("/proc/self/exe", buf, sizeof buf);
  if (n > 0)
  {
    result.assign(buf, n);
  }
  return result;
}

int ProcessInfo::openedFiles()
{
  t_numOpenedFiles = 0;
  scanDir("/proc/self/fd", fdDirFilter);
  return t_numOpenedFiles;
}

int ProcessInfo::maxOpenFiles()
{
  // struct rlimit
  // {
  //   rlim_t rlim_cur;
  //   rlim_t rlim_max;
  // };
  // rlim_cur是软上限，rlim_max是硬上限。软限制是内核强加给相应资源的限制值，硬限制是软限制的最大值。
  struct rlimit rl;
  if (::getrlimit(RLIMIT_NOFILE, &rl))
  {
    return openedFiles();
  }
  else
  {
    return static_cast<int>(rl.rlim_cur);
  }
}

ProcessInfo::CpuTime ProcessInfo::cpuTime()
{
  ProcessInfo::CpuTime t;
  struct tms tms;
  if (::times(&tms) >= 0)
  {
    const double hz = static_cast<double>(clockTicksPerSecond());
    t.userSeconds = static_cast<double>(tms.tms_utime) / hz;
    t.systemSeconds = static_cast<double>(tms.tms_stime) / hz;
  }
  return t;
}

int ProcessInfo::numThreads()
{
  int result = 0;
  string status = procStatus();
  size_t pos = status.find("Threads:");
  if (pos != string::npos)
  {
    result = ::atoi(status.c_str() + pos + 8);
  }
  return result;
}

std::vector<pid_t> ProcessInfo::threads()
{
  std::vector<pid_t> result;
  t_pids = &result;
  scanDir("/proc/self/task", taskDirFilter);
  t_pids = NULL;
  std::sort(result.begin(), result.end());
  return result;
}
